/**
 * 版权所有(C)，上海勾芒信息科技，2017，所有权利保留。
 * 
 * 项目名：	cards-web
 * 文件名：	WxMpCardxServiceImpl.java
 * 模块说明：	
 * 修改历史：
 * 2017年3月31日 - Debenson - 创建。
 */
package me.chanjar.weixin.mp.api.impl;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gomore.jeecg.common.Assert;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.JsonMapper;
import me.chanjar.weixin.mp.api.WxMpCardxService;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.card.AbstractCard;
import me.chanjar.weixin.mp.bean.card.ActivateMemberForm;
import me.chanjar.weixin.mp.bean.card.ActiveMemberRequest;
import me.chanjar.weixin.mp.bean.card.ApplyCardQrCode;
import me.chanjar.weixin.mp.bean.card.ApplyCardQrCodeResult;
import me.chanjar.weixin.mp.bean.card.BaseInfo.CardStatus;
import me.chanjar.weixin.mp.bean.card.Card;
import me.chanjar.weixin.mp.bean.card.Cash;
import me.chanjar.weixin.mp.bean.card.ConsumeCardResult;
import me.chanjar.weixin.mp.bean.card.Coupon;
import me.chanjar.weixin.mp.bean.card.Groupon;
import me.chanjar.weixin.mp.bean.card.Member;
import me.chanjar.weixin.mp.bean.card.MemberInfo;
import me.chanjar.weixin.mp.bean.card.QueryCardResult;
import me.chanjar.weixin.mp.bean.card.UserCards;

/**
 * 扩展卡券接口实现。
 * 
 * @author Debenson
 * @since 0.1
 */
public class WxMpCardxServiceImpl extends WxMpCardServiceImpl implements WxMpCardxService {
  static final Logger logger = LoggerFactory.getLogger(WxMpCardxServiceImpl.class);

  /** 微信卡券基准地址 */
  private static final String CARD_API_BASE_URL = "https://api.weixin.qq.com/card";

  private WxMpService wxMpService;

  public WxMpCardxServiceImpl(WxMpService wxMpService) {
    super(wxMpService);
    this.wxMpService = wxMpService;
  }

  @Override
  public <C extends AbstractCard> String createCard(C card) throws WxErrorException {
    if (card instanceof Cash) {
      return createCash((Cash) card);
    } else if (card instanceof Groupon) {
      return createGroupon((Groupon) card);
    } else if (card instanceof Coupon) {
      return createCoupon((Coupon) card);
    } else if (card instanceof Member) {
      return createMember((Member) card);
    } else {
      throw new RuntimeException("暂不支持的卡券类型: " + card.getClass().getName());
    }
  }

  @Override
  public String createCash(Cash cash) throws WxErrorException {
    Card card = new Card();
    card.setCardType("CASH");
    card.setCash(cash);
    return createCard(card);
  }

  @Override
  public String createGroupon(Groupon groupon) throws WxErrorException {
    Card card = new Card();
    card.setCardType("GROUPON");
    card.setGroupon(groupon);
    return createCard(card);
  }

  /**
   * 创建优惠券
   *
   * @param coupon
   * @return
   */
  @Override
  public String createCoupon(Coupon coupon) throws WxErrorException {
    Card card = new Card();
    card.setCardType("GENERAL_COUPON");
    card.setGeneralCoupon(coupon);
    return createCard(card);
  }

  @Override
  public String createMember(Member member) throws WxErrorException {
    Card card = new Card();
    card.setCardType("MEMBER_CARD");
    card.setMember(member);
    return createCard(card);
  }

  @Override
  public Card get(String cardId) throws WxErrorException {
    String postData = "{\"card_id\":\"%s\"}";
    String url = CARD_API_BASE_URL + "/get";
    String response = this.wxMpService.post(url, String.format(postData, cardId));

    CardWrapper result = JsonMapper.defaultMapper().fromJson(response, CardWrapper.class);
    return result.getCard();
  }

  @Override
  public void modifystock(String cardId, int occur) throws WxErrorException {
    Assert.assertArgumentNotEmpty(cardId, "cardId");

    Map<String, Object> params = Maps.newHashMap();
    params.put("card_id", cardId);
    if (occur > 0) {
      params.put("increase_stock_value", occur);
    } else {
      params.put("reduce_stock_value", -occur);
    }

    String url = CARD_API_BASE_URL + "/modifystock";
    String postData = JsonMapper.defaultMapper().toJson(params);
    this.wxMpService.post(url, postData);
  }

  @Override
  public void setWhiteListByOpenId(String... openIds) throws WxErrorException {
    setWhiteList(Lists.newArrayList(openIds), Lists.newArrayList(new String[] {}));
  }

  @Override
  public void setWhiteListByUserName(String... userNames) throws WxErrorException {
    setWhiteList(Lists.newArrayList(new String[] {}), Lists.newArrayList(userNames));
  }

  @Override
  public void setWhiteList(List<String> openIds, List<String> userNames) throws WxErrorException {
    Map<String, Object> request = new HashMap<>();
    if (openIds != null && !openIds.isEmpty()) {
      request.put("openid", openIds);
    }
    if (userNames != null && !userNames.isEmpty()) {
      request.put("username", userNames);
    }

    String url = CARD_API_BASE_URL + "/testwhitelist/set";
    String postData = JsonMapper.defaultMapper().toJson(request);
    this.wxMpService.post(url, postData);
  }

  @Override
  public QueryCardResult query(int offset, int count, CardStatus... statusList)
      throws WxErrorException {
    Map<String, Object> request = new HashMap<>();
    request.put("offset", offset);
    request.put("count", count);
    if (statusList != null && statusList.length > 0) {
      request.put("status_list", statusList);
    }

    String url = CARD_API_BASE_URL + "/batchget";
    String postData = JsonMapper.defaultMapper().toJson(request);
    String response = this.wxMpService.post(url, postData);

    QueryCardResult result = JsonMapper.defaultMapper().fromJson(response, QueryCardResult.class);
    return result;
  }

  @Override
  public ApplyCardQrCodeResult applyCardQrCode(ApplyCardQrCode... applies) throws WxErrorException {
    Assert.assertArgumentNotNull(applies, "applies");

    if (applies.length == 1) {
      return createSingleCardQrCode(applies[0]);
    } else {
      return createMultiCardsQrCode(applies);
    }
  }

  @Override
  public void depositCode(String cardId, List<String> codes) throws WxErrorException {
    Assert.assertArgumentNotEmpty(cardId, "cardId");
    Assert.assertArgumentNotNull(codes, "codes");

    Map<String, Object> request = new HashMap<>();
    request.put("card_id", cardId);
    request.put("code", codes);

    String url = CARD_API_BASE_URL + "/code/deposit";
    String postData = JsonMapper.defaultMapper().toJson(request);
    this.wxMpService.post(url, postData);
  }

  @Override
  public int getDepositCodeCount(String cardId) throws WxErrorException {
    Assert.assertArgumentNotEmpty(cardId, "cardId");

    Map<String, Object> request = new HashMap<>();
    request.put("card_id", cardId);

    String url = CARD_API_BASE_URL + "/code/getdepositcount";
    String postData = JsonMapper.defaultMapper().toJson(request);
    String response = this.wxMpService.post(url, postData);

    GetDepositCodeCountResult result = JsonMapper.defaultMapper().fromJson(response,
        GetDepositCodeCountResult.class);
    return result.getCount();
  }

  @Override
  public UserCards getUserCards(String openId, String cardId) throws WxErrorException {
    Assert.assertArgumentNotEmpty(openId, "openId");

    Map<String, Object> params = Maps.newHashMap();
    params.put("openid", openId);
    params.put("card_id", cardId);

    String url = CARD_API_BASE_URL + "/user/getcardlist";
    String postData = JsonMapper.defaultMapper().toJson(params);
    String response = this.wxMpService.post(url, postData);
    UserCards result = JsonMapper.defaultMapper().fromJson(response, UserCards.class);
    return result;
  }

  @Override
  public ConsumeCardResult consumeCard(String code) throws WxErrorException {
    return consumeCard(code, null);
  }

  @Override
  public ConsumeCardResult consumeCard(String code, String cardId) throws WxErrorException {
    Assert.assertArgumentNotEmpty(code, "code");

    String url = CARD_API_BASE_URL + "/code/consume";
    Map<String, Object> params = Maps.newHashMap();
    params.put("code", code);
    params.put("card_id", cardId);
    String postData = JsonMapper.defaultMapper().toJson(params);

    String response = this.wxMpService.post(url, postData);
    ConsumeCardResult result = JsonMapper.defaultMapper().fromJson(response,
        ConsumeCardResult.class);
    return result;
  }

  @Override
  public void activeMember(ActiveMemberRequest request) throws WxErrorException {
    Assert.assertArgumentNotNull(request, "request");

    String url = CARD_API_BASE_URL + "/membercard/activate";
    String postData = JsonMapper.defaultMapper().toJson(request);
    this.wxMpService.post(url, postData);
  }

  @Override
  public void setActivateMemberForm(ActivateMemberForm form) throws WxErrorException {
    Assert.assertArgumentNotNull(form, "form");

    String url = CARD_API_BASE_URL + "/membercard/activateuserform/set";
    String postData = JsonMapper.defaultMapper().toJson(form);
    this.wxMpService.post(url, postData);
  }

  @Override
  public MemberInfo getMemberInfo(String code, String cardId) throws WxErrorException {
    Assert.assertArgumentNotEmpty(code, "code");

    Map<String, Object> params = Maps.newHashMap();
    params.put("code", code);
    params.put("card_id", cardId);
    String postData = JsonMapper.defaultMapper().toJson(params);

    String url = CARD_API_BASE_URL + "/membercard/userinfo/get";
    String response = this.wxMpService.post(url, postData);
    MemberInfo result = JsonMapper.defaultMapper().fromJson(response, MemberInfo.class);
    return result;
  }

  private ApplyCardQrCodeResult createSingleCardQrCode(ApplyCardQrCode apply)
      throws WxErrorException {
    Assert.assertArgumentNotNull(apply, "apply");

    Map<String, Object> map = Maps.newHashMap();
    map.put("action_name", "QR_CARD");
    map.put("expire_seconds", apply.getExpireSeconds());
    Map<String, Object> actionInfoMap = Maps.newHashMap();
    Map<String, Object> cardMap = Maps.newHashMap();
    actionInfoMap.put("card", cardMap);
    map.put("action_info", actionInfoMap);
    cardMap.put("code", apply.getCode());
    cardMap.put("card_id", apply.getCardId());
    cardMap.put("openid", apply.getOpenId());
    cardMap.put("is_unique_code", apply.getIsUniqueCode());
    cardMap.put("outer_id", apply.getOuterId());
    cardMap.put("outer_str", apply.getOuterStr());

    String url = CARD_API_BASE_URL + "/qrcode/create";
    String postData = JsonMapper.defaultMapper().toJson(map);
    String response = this.wxMpService.post(url, postData);

    ApplyCardQrCodeResult result = JsonMapper.defaultMapper().fromJson(response,
        ApplyCardQrCodeResult.class);
    return result;
  }

  private ApplyCardQrCodeResult createMultiCardsQrCode(ApplyCardQrCode... applies)
      throws WxErrorException {
    Map<String, Object> map = Maps.newHashMap();
    map.put("action_name", "QR_MULTIPLE_CARD");
    Map<String, Object> actionInfoMap = Maps.newHashMap();
    Map<String, Object> multipleCardMap = Maps.newHashMap();
    List<Map<String, Object>> cardList = Lists.newArrayList();
    multipleCardMap.put("card_list", cardList);
    actionInfoMap.put("multiple_card", multipleCardMap);
    map.put("action_info", actionInfoMap);

    for (ApplyCardQrCode apply : applies) {
      Map<String, Object> cardMap = Maps.newHashMap();
      cardList.add(cardMap);

      cardMap.put("code", apply.getCode());
      cardMap.put("card_id", apply.getCardId());
      cardMap.put("is_unique_code", apply.getIsUniqueCode());
      cardMap.put("outer_id", apply.getOuterId());
      cardMap.put("outer_str", apply.getOuterStr());
    }

    String url = CARD_API_BASE_URL + "/qrcode/create";
    String postData = JsonMapper.defaultMapper().toJson(map);
    String response = this.wxMpService.post(url, postData);
    ApplyCardQrCodeResult result = JsonMapper.defaultMapper().fromJson(response,
        ApplyCardQrCodeResult.class);
    return result;
  }

  /**
   * 创建卡券。
   * 
   * @param card
   * @return
   * @throws WxErrorException
   */
  private String createCard(Card card) throws WxErrorException {
    String postData = JsonMapper.nonDefaultMapper().toJson(new CardWrapper(card));
    String url = CARD_API_BASE_URL + "/create";
    String response = this.wxMpService.post(url, postData);
    Map<String, Object> result = JsonMapper.nonDefaultMapper().json2Map(response);
    return (String) result.get("card_id");
  }

  /**
   * @author Debenson
   * @since 0.1
   */
  static class CardWrapper implements Serializable {
    private static final long serialVersionUID = -5774502383081514587L;

    private Card card;

    public CardWrapper() {
    }

    public CardWrapper(Card card) {
      this.card = card;
    }

    public Card getCard() {
      return card;
    }

    public void setCard(Card card) {
      this.card = card;
    }

  }

  /**
   * @author Debenson
   * @since 0.1
   */
  static class GetDepositCodeCountResult implements Serializable {
    private static final long serialVersionUID = -3770327573617769352L;

    private int count;

    public int getCount() {
      return count;
    }

    public void setCount(int count) {
      this.count = count;
    }

  }

}
